package xyz.lwm.lazycat.beans;

import lombok.AllArgsConstructor;
import xyz.lwm.lazycat.utility.It;

import java.util.List;
import java.util.stream.Collectors;

/**
 * <h1>Bean 的创建与处理</h1>
 * <p>通过状态和频次的控制, 来解决循环依赖和循环构造的问题</p>
 *
 * <h3>整体说明:</h3>
 * <pre>process 方法和 init 方法一起完成 getBean 操作, 并完成 bean 的生命周期过程;
 * 解决了 bean 的依赖顺序以及相互依赖的操作问题</pre>
 * <p>process 中, 最先处理依赖注入, 最后处理 Setting 设置, 中间的 bean post processor 可能
 * 是创建代理对象.</p>
 *
 * <h3>流程说明:</h3>
 * <h5>1. 构造依赖处理</h5>
 * <p>A 构造依赖 B: </p>
 * <p>init A (构造时需要 B ) --> init B --> process B --> init A --> process A --> A (完成)   </p>
 * <p>简言之: A 创建依赖 B, 先把 B 创建处理结束(生命周期完成)后, 再接着创建 A, 然后处理 A</p>
 *
 * <h5>2. 注入依赖处理</h5>
 * <p>A 注入依赖 B: </p>
 * <p>init A --> process A (注入时需要 B ) --> init B --> process B --> process A --> A (完成)  </p>
 * <p>简言之: A 已创建, 但在处理时需要注入 B, 须先创建 B 并处理 B, 再接着处理 A, 完成 A 的生命周期</p>
 *
 * <h5>3. 构造相互依赖</h5>
 * <p>A 构造依赖 B, B 构造依赖 A :</p>
 * <p>init A --> init B --> init A --> init B --> init A ....</p>
 * <p>简言之: 创建 A 和 B 的时候相互需要对方, 没完没了, 从 {@link BeanSupplier#init()}中可以看出,
 * if (initDoingTimes > 1) 时会抛出异常, 即 init 方法最多能进入三次</p>
 *
 * <h5>4. 注入相互依赖</h5>
 * <p>A 注入 B, B 注入 A :</p>
 * <p>init A (完成) --> process A (依赖 B) --> init B (完成) --> process B (依赖 A) --> (第二次) process A (依赖 B)
 * --> (第二次) process B (依赖 A) -> (第三次) process A (空转并退出) -> getBean A 完成 (A 并未注入 B) --> 回到 (第二次) process B
 * --> B 注入 A (A 成 B 的属性) --> getBean B 完成 --> 回到 (第二次) process A --> A 注入 B (B 成为 A 的属性) -->
 * 回到 process B --> if (processState == DOING) 判断此次 process B 空转, 因为 B 已处理完成
 * --> 回到 process A (一样空转, 已处理过) --> 最终完成 A, B 的相互注入依赖 </p>
 * <p>简言之: 查看源码并结合着 processState 和 processDoingTimes, 可以推演上面流程. </p>
 * <p>问: 为什么允许 process 方法进入三次？</p>
 * <p>答: 实际上第三次进入就是空转，是为了退出循环的，前两次才是业务逻辑</p>
 *
 * <h5>5. Bean 对象注入自身</h5>
 * <p>Bean 注入自身 (非 aop 对象): </p>
 * <p>
 * init A (完成) --> process A (依赖 A) --> getBean A --> (第二次) process A --> getBean A
 * --> (第三次) process A (空转) --> 回到 getBean A (得到 A) --> 回到 (第二次) process A --> A 注入 A
 * --> ... -> 回到 process A (依赖 A) --> 空转 --> 整体完成流程
 * </p>
 * <p>简言之: 通过第三次 process (空转) 退出循环, 然后完成注入</p>
 *
 * <h5>6. Bean 注入自身的代理对象</h5>
 * <p>Bean 注入自身 (aop 创建的代理对象): </p>
 * <p>
 * init A (完成) --> process A (依赖注入 A) --> (第一次) getBean A --> init A (跳过) --> (第二次) process A
 * --> getBean A --> (第三次) process A (空转) --> 回到 getBean A (得到 A, A 未注入)
 * --> 回到 (第二次) process A 中的注入部分 --> A 注入 A --> 代理 processor 继续处理 --> A 换成 代理对象 A1
 * --> 回到 (第一次) getBean A (得到 A1) --> 回到 process A (A 注入 A1, A 的属性已经是代理对象)
 * --> process A 中 代理 process 继续处理 --> A 代理成新的对象 A2
 * --> bean 已经是 A1 了, 所以 A2 忽略 (对应 if (processState == DOING) 时才替换 bean)
 * </p>
 * <p>简言之: 通过第三次 process (空转) 退出循环, process 内部并没有判断状态, 即只要进入 beanProcessor.process(...) 方法
 * 就会完成注入、代理等一系列操作, 则出现 A 先注入 A, 再注入 A1 这样的情况</p>
 *
 * <h5>7. 构造依赖和注入依赖同时出现 </h5>
 * <p>A 注入依赖 B, B 构造依赖 A: </p>
 * <p>
 * init A (完成) --> process A (依赖 B) --> getBean B --> init B (依赖 A) --> getBean A --> init A (忽略) --> (第二次) process A
 * --> (第二次) getBean B --> (第二次) init B --> (第二次)  getBean A --> (第三次) process A (空转) --> 回到 (第二次)  getBean A (得到 A)
 * --> 回到 (第二次) init B (B 创建成功) --> process B (完成) -->  (第二次) getBean B (完成) -->
 * --> 回到 (第二次) process A --> A 注入 B (B 成为 A 的属性) --> getBean A 完成 --> ... 前面均完成
 * </p>
 *
 * <h3>补充说明: </h3>
 * <h5>init 方法说明</h5>
 * <pre>
 * init 方法最多进入三次, 第四次就抛异常, 表示构造时出现循环依赖;
 * 为什么 init 方法可以进入三次? 因为 process 方法可以进入三次(第三次为空转), init 在 process 前, 如果不是三次, process 就不能有效处理两次.
 * </pre>
 * <h5>process 方法说明</h5>
 * <pre>
 * process 方法可以进入三次, 前两次是有效进入, 第三次是空转, 退出依赖查找, 这样可以先获取到没有 process 的对象;
 * 为什么 process 方法可以有效进入两次?
 * 简单来说, process 有效进入一次可以解决正常依赖、相互依赖、依赖自身的场景,
 * process 有效进入两次可以解决注入自身代理对象的场景.
 * </pre>
 *
 * @author lwm
 * @param <T>
 */
class BeanSupplier<T> implements BeanRef<T> {

    private static final int TODO = 0;

    private static final int DOING = 1;

    private static final int DONE = 2;

    // bean design
    private final BeanDesign<T> design;

    // bean ref manager
    private final BeanRefs beanRefs;

    // bean processor
    private final BeanProcessor beanProcessor;

    // bean object
    private T bean;

    // bean 是否已创建 (0: 未初始化; 1: 开始初始化; 2: 已完成初始化)
    private int initState = TODO;

    // bean 初始化次数 (因为复杂的依赖导致多次进入 init 方法, 且 init 未完成)
    private int initDoingTimes;

    // bean 是否已处理 (0: 未处理; 1: 开始处理; 2: 已完成处理)
    private int processState = TODO;

    // bean 处理次数 (因为复杂的依赖导致多次进入 process 方法, 且 process 未完成)
    private int processDoingTimes;

    public BeanSupplier(BeanDesign<T> design, BeanRefs beanRefs, BeanProcessor beanProcessor) {
        this.design = design;
        this.beanRefs = beanRefs;
        this.beanProcessor = beanProcessor;
    }

    @Override
    public BeanDesign<T> getDesign() {
        return design;
    }

    /**
     * <h3>bean 的创建与处理, 最后获取 bean</h3>
     * <p>bean 的创建和处理过程:</p>
     * <li>1. 已创建且已处理的, 直接获取 bean;</li>
     * <li>2. 已创建但未处理的, 处理后返回 bean;</li>
     * <li>3. 未创建且未处理的, 先创建, 再处理, 最后返回 bean</li>
     * <br>
     * <p>getBean是 bean 的完整的生命周期, 从创建到处理完成, 其中若涉及到依赖的其他 bean,
     * 在获取依赖 bean 的过程中, 会将依赖 bean 处理完成, 才处理当前 bean.</p>
     * <p>简言之: A 依赖 B, 必须等 B 创建处理完成后, 再接着处理 A</p>
     */
    @Override
    public T getBean() {
        if (initState == DONE) {
            // 已创建 + 已处理 => 直接返回
            if (processState == DONE) {
                return bean;
            }
            // 已创建, 但未处理 => 处理 bean (若 bean processor 未排序则不处理)
            this.process();
            return It.of(bean).getOrThrow(() -> new BeansException("bean instantiate error"));
        }
        // 未创建 => 创建 bean, 处理 bean(可能未处理成功)
        this.init();
        this.process();
        return It.of(bean).getOrThrow(() -> new BeansException("bean instantiate error"));
    }

    /**
     * 初始化 bean, 前提是该 bean 未开始初始化 <br>
     */
    @SuppressWarnings("unchecked")
    private void init() {
        if (initState == DOING) {
            // 进入 init 方法次数过多, 说明有循环构造依赖, 须检查
            // initDoingTimes > 1 就报错, 即 initDoingTimes 可以为 0, 1
            // 所以 init 方法可以进入三次, 第四次就报错
            if (initDoingTimes > 1) {
                InstantiableCircleUtil.checkCircle(design.getInstantiable());
            }
            initDoingTimes++;
        }
        if (initState != DONE) {
            try {
                initState = DOING;
                Instantiable instantiable = design.getInstantiable();
                InstantiableCircleUtil.push(instantiable);
                T bean = (T) instantiable.instantiate(design.getDependsOn(), beanRefs);
                // 复杂的创建过程会导致 bean 已经完成创建, 因此只可创建一次
                // 不能再次赋值, 否则导致 bean post processor 的处理被还原, 如已经代理过的 bean 又被覆盖成非代理类
                if (initState == DOING) {
                    this.bean = bean;
                }
                initState = DONE;
            } finally {
                InstantiableCircleUtil.remove();
            }
        }
    }

    /**
     * <p>处理 bean, 通过 beanProcessor 的 process 方法工作</p>
     * bean 处理有两个前提: 一是 bean 对象已创建; 二是 beanProcessor 已排序.
     * 第二个前提的原因是: 容器在 bean processor 初始化期间, 可能有的 bean 会初始化, 此时是不允许的
     * 但 bean processor 初始化完成后, 容器后期会对每一个 bean 补偿处理;
     */
    @SuppressWarnings("unchecked")
    private void process() {
        // bean 完成创建后才能处理, 同时要求 bean processor 已经排序
        if (initState == DONE && beanProcessor.isSorted()) {
            // 循环依赖时, 二次进入此方法,
            // 将会提前创建代理对象, 使得注入自身的代理对象得以实现
            // 第三次进入 process 时, 退出
            if (processState == DOING && processDoingTimes == 2) {
                return;
            }
            if (processState != DONE) {
                // 开始处理 (状态控制, 防止死循环)
                processState = DOING;
                processDoingTimes++;

                // bean 处理后会替换原 bean
                T processed = (T) beanProcessor.process(bean, design.getName());
                // process 重复处理时, 只有一次生效, 避免覆盖 (因为 process 过程中没有判断状态, 代理对象可能创建了两次, 但只能选一次)
                if (processState == DOING) {
                    bean = processed;
                }

                // 处理完成
                processState = DONE;
            }
        }
    }

}

/**
 * bean array supplier, for array dependency or array injection
 *
 * @author lwm
 */
@AllArgsConstructor
class ListBeanRef implements BeanRef<List<?>> {

    private final List<BeanRef<?>> beanRefList;

    @Override
    public BeanDesign<List<?>> getDesign() {
        throw new UnsupportedOperationException();
    }

    @Override
    public List<?> getBean() {
        return beanRefList.stream().map(BeanRef::getBean).collect(Collectors.toList());
    }

}
